<?php

// NetworkObject.php
// Michael Baker
// 2010.06.21
//
// implements a hierarchy of network objects

if ( !defined( "IN_MDM" ) )
  exit;
  
require_once( "Fault.php" );

class Node
{
  private $id;     // ID
  private $addr;   // address
  private $has_db; // has database? 
  private $title; // 2-element array
  
  public function __construct( $addr, $has_db )
  {
    $this->addr = $addr;
    $this->has_db = $has_db;
    $this->title = array();
  }
    
  public function qdb_suffices( $addr )
  {
    // ask the node if its database suffices for information
    // between two addresses
    
    if ( !$this->get_has_db() )
    {
      return false;
    }
    else
    {
      $qdb = $this->get_qdb();
      $qdb->set_date( get_ymd( time() ) );
      
      // ask the DB object itself
      $has_data = $qdb->has_data( $addr );
      $qdb->close();
      
      return $has_data;
    }
  }

  public function responds()
  {	
    // does this node respond?
    
    return device_responds($this->get_address());
  }
  
  public function get_qdb()
  {
    // get a query database for this node
    
    $addr = $this->get_address();
    
    if ( !$this->get_has_db() )
      throw new Exception( "Failed to get QDB object: Node at $addr has no database" );
    
    if ( !$this->responds() )
      throw new Exception( "Failed to get QDB object: Connection timed out to node at $addr" );
    
    return new QueryDatabase( $addr ); 
  }
  
  public function get_has_db()            { return $this->has_db;          }
  public function get_address()           { return $this->addr;            }
  public function get_id()                { return $this->id;              }
  public function set_id( $id )           { $this->id = $id;               }
  public function get_title( $index = 1 ) { return $this->title[ $index ]; }
  public function set_title( $title )     { $this->title = $title;         }
}

class NodeCouple
{
  private $id;         // ID
  private $initial;    // initial node
  private $final;      // final node
  private $db_flag;    // boolean value. if 0, initial has db. if 1, final has db.
  private $faults;     // array of Fault objects
  private $colorcodes; // array of two color codes (RX_CC, TX_CC)
  private $is_down;    // boolean value indicating whether or not the segment is completely down
  
  public function __construct( $initial, $final )
  {
    $this->is_down = false;
    
    $this->initial = $initial;
    $this->final   = $final;
    
    // Initialize faults here
    $this->faults  = array();
    
    // This should come from a database eventually (no need for it to be hardcoded)
    $fault_loss  = new Fault( "f_loss", $this );
    $fault_delay = new Fault( "f_delay", $this );
    $fault_pdv   = new Fault( "f_pdv", $this );
    
    $fault_loss->set_id( 0 );
    $fault_delay->set_id( 1 );
    $fault_pdv->set_id( 2 );
    
    $fault_loss->set_title( "LOSS" );
    $fault_delay->set_title( "REL. DELAY" );
    $fault_pdv->set_title( "JITTER" );
    
    array_push( $this->faults, $fault_loss, $fault_delay );
  }
    
  public function process( $timestamp_pair )
  {
    // process for a certain date
    
    $stime = $timestamp_pair[ 0 ];
    $etime = $timestamp_pair[ 1 ];
    
    if ( !( $etime > $stime ) )
      throw new Exception( "Failed to process nodecouple: query start time is not before end time" );

    $ymd = get_ymd( $stime );
    $ymd2 = get_ymd( $etime );
    
    $initial_node = $this->initial;
    $final_node   = $this->final;
    
    try
    {
      $qdb = $this->get_db_conn();
    }
    catch (Exception $e)
    {
      // Segment datasource is "down".
      
      $this->is_down = true;
      return;
    }
    
    $qdb->set_date( $ymd2 );
    
    $rx_cc = array(); // each element in here is a fault color for that direction
    $tx_cc = array(); // etc
    
    foreach ( $this->faults as $faultObject )
    {
      $faultObject->process( $qdb, $timestamp_pair ); // it can get the nodes from its parent
      
      $ccArray = $faultObject->get_colorcodes();
      
      $rx_cc[] = $ccArray[ 0 ];
      $tx_cc[] = $ccArray[ 1 ];
    }
    
    $qdb->close();
    
    // Return an array in the form [ [c1, c2, c3], [c1, c2, c3] ]
    // dimension 1: direction; dimension 2: fault
    
    $worst_rx = get_worst_color( $rx_cc );
    $worst_tx = get_worst_color( $tx_cc );
    
    $this->colorcodes = array( $worst_rx, $worst_tx );
  }
  
  private function get_db_node()
  {
    return ($this->db_flag) ? $this->final : $this->initial;
  }
  
  public function get_db_conn()
  {
    $node = $this->get_db_node();
    return $node->get_qdb();
  }
  
  public function is_down()            { return $this->is_down;                        }
  public function get_id()             { return $this->id;                             }
  public function set_id( $id )        { $this->id = $id;                              }
  public function get_title()          { return $this->title;                          }
  public function set_title( $title )  { $this->title = $title;                        }
  public function get_db_flag()        { return $this->db_flag;                        }
  public function set_db_flag( $flag ) { $this->db_flag = $flag;                       }
  public function get_nodes()          { return array( $this->initial, $this->final ); }
  public function get_colorcodes()     { return $this->colorcodes;                     }
  public function get_faults()         { return $this->faults;                         }
}

class Workflow
{
  private $children;
  private $id;
  private $colorcodes;
  
  public function __construct( $children )
  {
    $this->children = $children;  
  }
  
  public function process( $timestamp_pair )
  {
    // process everything
    $rx_cc = array();
    $tx_cc = array();

    foreach ( $this->children as $nodecouple )
    { 
      $nodecouple->process( $timestamp_pair );
      
      // Check if it's dead or not
      if ( $nodecouple->is_down() )
      {
        $rx_cc[] = COLOR_DEAD;
        $tx_cc[] = COLOR_DEAD;
        
        continue;
      }
      
      // Get the worst colorcodes for this segment across all faults
      $cc = $nodecouple->get_colorcodes();
      
      $rx_cc[] = $cc[ 0 ];
      $tx_cc[] = $cc[ 1 ];
    }
    
    // Calculate the worst colorcodes for the whole workflow
    $rx_code = get_worst_color( $rx_cc );
    $tx_code = get_worst_color( $tx_cc );
    
    $this->colorcodes = array( $rx_code, $tx_code );
  }  
  
  public function get_endpoints()
  {
    $couples = $this->children;
    
    $couple_count = count($couples);
    $last_couple = $couple_count - 1;
    $first_couple = 0;
    
    $c1 = $couples[ $first_couple ];
    $c2 = $couples[ $last_couple ];
    
    $c1_nodes = $c1->get_nodes();
    $initial = $c1_nodes[ 0 ];
    
    $c2_nodes = $c2->get_nodes();
    $final = $c2_nodes[ 1 ];
    
    return array( $initial, $final );
  }
  
  public function get_seg_colorcodes( $segment_index )
  {
    // get the colorcodes for a certain segment
    
    $retval = array();
    $segments = $this->get_children();
    $seg_count = count( $segments );
    $last_index = $seg_count - 1;
    
    if ( $segment_index > $last_index )
      throw new Exception( "Failed to get segment colorcodes: segment $segment_index requested, only $seg_count segments exist" );
    
    $segment  = $segments[ $segment_index ];
    $faults = $segment->get_faults();
    
    foreach ( $faults as $fault )
    {
      $cc_array = ($segment->is_down()) ? array(-1,-1) : $fault->get_colorcodes();
      $retval[] = $cc_array;
    }
    
    return $retval;
  }
  
  public function get_fault_names()
  {
    // get the fault names
    
    $retval = array();
    $segments = $this->get_children();
    if ( !$segments )
      throw new Exception( "Failed to get fault names: no segments found in workflow" );
    
    // we only examine the fault titles from the first segment, as the
    // fault titles for the other ones should be the same
    $segment = $segments[ 0 ];
    $faults = $segment->get_faults();
    
    foreach ( $faults as $fault )
      $retval[]   = $fault->get_title();
    
    return $retval;
  }
  
  public function get_seg_names()
  {
    // get segment names
    
    $retval = array();
    $segments = $this->get_children();
    
    foreach ( $segments as $segment )
      $retval[] = $segment->get_title();
    
    return $retval;
  }
  
  public function get_fault_numerics( $segment_index )
  {
    // get the fault numerics for each fault in the segment
    
    $retval = array();
    $segments = $this->get_children();
    $seg_count = count( $segments );
    $last_index = $seg_count - 1;
    
    if ( $segment_index > $last_index )
      throw new Exception( "Failed to get fault numerics: segment $segment_index requested, only $seg_count segments exist" );
    
    $segment = $segments[ $segment_index ];
    $faults  = $segment->get_faults();
    
    foreach ( $faults as $fault )
      $retval[]  = $fault->get_numerics();
    
    return $retval;
  }
  
  public function get_fault_extras( $segment_index )
  {
    // get the fault extras for each fault in the segment
    
    $retval = array();
    $segments = $this->get_children();
    $seg_count = count( $segments );
    $last_index = $seg_count - 1;
    
    if ( $segment_index > $last_index )
      throw new Exception( "Failed to get fault extras: segment $segment_index requested, only $seg_count segments exist" );
    
    $segment = $segments[ $segment_index ];
    $faults  = $segment->get_faults();
    
    foreach ( $faults as $fault )
    {
      $fault_arr = $fault->get_extras();
      array_push( $retval, $fault_arr );
    }
    
    return $retval;    
  }
        
  public function get_title()
  {
    $nodecouples = count( $this->get_children() );
    
    $last = $nodecouples - 1;
    $first = 0;
    
    $first_couple = $this->children[ $first ];
    $last_couple = $this->children[ $last ];
    
    $first_couple = $first_couple->get_nodes();
    $last_couple = $last_couple->get_nodes();
    
    $first_node = $first_couple[ 0 ];
    $last_node = $last_couple[ 1 ];
    
    $first_title = $first_node->get_title();
    $last_title = $last_node->get_title();
    
    return "$first_title -> $last_title";
  }
  
  public function get_colorcodes() { return $this->colorcodes; }
  public function get_id()         { return $this->id;         }
  public function set_id( $id )    { $this->id = $id;          }  
  public function get_children()   { return $this->children;   }
}

?>
